Text Editing
This programming recipe allows the user to edit text in a layout shape by pressing a key when there is a layout shape with a selection.This recipe discusses three types of keypresses: arrow keys, the Delete key,
and character keys. For each type of keypress, this recipe shows how to respond if the current selection is a caret selection and how to respond if
it's a range selection.Overview of Recipe Steps
The steps in this recipe show you how to:
You need to follow all of the steps of this recipe to allow your users to edit a layout shape.
- Determine if the user pressed a key
- Handle arrow keys
- Handle the Delete key
- Handle character keys
Functions Used in This Recipe
QuickDraw GX functions used in this recipe:
GXDrawShape
"Shape Objects"
QuickDraw GX ObjectsGXGetLeftVisualOffset
"Layout Carets, Highlighting, and
Hit-Testing"
QuickDraw GX TypographyGXGetRightVisualOffset
"Layout Carets, Highlighting, and
Hit-Testing"
QuickDraw GX TypographyGXSetLayoutParts
"Layout Shapes"
QuickDraw GX TypographyStandard Macintosh functions used in this recipe:
WaitNextEvent
"Event Manager"
Macintosh Toolbox EssentialsThis recipe gives a brief description of these functions; you can find complete reference information for these functions in the Inside Macintosh suite of books.
This recipe also uses functions from the QuickDraw GX libraries:
LockEditHandle
layout edit library NewSelectionAndHighlight
layout edit library GetSelectionType
selection library GetCaretSelection
selection library GetPreviousOffset
layout library Recipe Step Descriptions
In this section, each step is described individually.
- Determine if the user pressed a key
You can determine if the user of your application has pressed a key by examining the
what
field of the event record returned by the standard Macintosh functionWaitNextEvent
. If the value of this field iskeyDown
orautoKey
, you should determine if the user is trying to edit a layout shape.As a simple example, you might have a global variable named
gCurrentlySelectedLayout
, which contains a reference to the currently selected layout if one is selected andnil
if not. You could then determine
if the user is trying to edit a layout shape simply by examining if there
is a currently selected layout, as with this code:
if (gCurrentlySelectedLayout != nil) {
MyHandleLayoutKeyPress(gCurrentLayoutEditHandle,
theEvent->message & charCodeMask);
}In your
MyHandleLayoutKeyPress
function, you need to determine what type of key was pressed--for example, an arrow key, the Delete key, or a character key--and respond accordingly.Here is the flow of control for the
MyHandleLayoutKeyPress
function:
void MyHandleLayoutKeyPress(LayoutEditHandle handle,
char key)
{
LayoutEditPtr myLayoutEdit = LockEditHandle(handle);
switch (key) {
case leftArrow:
case rightArrow: {
/* Handle arrow keys. See Step 2. */
break;
}
case backSpace: {
/* Handle Delete key. See Step 3. */
break;
}
default: {
/* Handle character keys. See Step 4. */
break;
}
}The next few steps, Step 2 through Step 4, show you how to handle each type of keypress.
- Handle arrow keys
If the user pressed an arrow key, you need to
- erase the current highlight shape
- update the selection stored in your layout edit shape
- recalculate the highlight shape
- draw the new highlight shape
To erase the current highlight shape, you simply have to draw it again using the
GXDrawShape
function:
GXDrawShape(myLayoutEdit->highlight); /* erase previous */How you update the selection stored in your layout edit shape depends on whether the user pressed the Left Arrow key or the Right Arrow key. In either case, the new selection is a caret selection.
If, for instance, the user pressed the Left Arrow key, the new selection is the offset that visually appears one character to the left of the old selection. QuickDraw GX provides the function
GXGetLeftVisualOffset
for just
this purpose.First, you need to determine the offset that corresponds to the left edge of the current selection. You can retrieve this information from your layout edit structure, thus:
SelectionOffset oldOffset;oldOffset = myLayoutEdit->selectionRanges.ranges[0].minOffset;
The
selectionRanges
field of the layout edit structure is an array that contains aranges
field, which is itself an array of selection ranges. Each element in this array contains two fields:- The
minOffset
field specifies the offset at the beginning of a selection.- The
maxOffset
field specifies the offset at the end of a selection.Although the
selectionRanges
field allows you to provide for multiple-
range selections, the example in this recipe only allows for single-range selections, so only the first element of the array is used.Once you've found the offset that corresponds to the left edge of the previous selection, you can determine the offset that visually appears
one character to the left:
newOffset = GXGetLeftVisualOffset(myLayoutEdit->layout,
oldOffset);Similarly, if the user pressed the Right Arrow key, you find the offset corresponding to the right edge of the previous selection and then
calculate the offset that visually appears one character to the right:
oldOffset = myLayoutEdit->selectionRanges.ranges[0].maxOffset;
newOffset = GXGetLeftVisualOffset(myLayoutEdit->layout,
oldOffset);
}Once you've found the new offset, you can call the library function
NewSelectionAndHighlight
function to store the new selection in your layout edit structure and calculate a new highlight shape. You can finish
by drawing the highlight:
NewSelectionAndHighlight(myLayoutEdit, newOffset, newOffset);
GXDrawShape(myLayoutEdit->highlight);
- Handle the Delete key
If the user pressed the Delete key, you need to determine if the current selection is a caret selection or a range selection. If it's a caret selection, you want to delete the character in the text before the current offset. If it's a range selection, you delete the characters in the current selection.
You can determine the selection type using the
GetSelectionType
library function:
SelectionType selectionType;
selectionType = GetSelectionType(myLayoutEdit->selection);If the value returned by this function is
simpleCaret
, you need to delete
the character before the current offset. You can use the library functionGetCaretSelection
to find the current offset, the library functionGetPreviousOffset
to find the previous offset, and the QuickDraw GX function GXSetLayoutParts to delete the character:
SelectionOffset oldOffset;
oldOffset = GetCaretSelection(myLayoutEdit->selection, nil);
if (caret > 0) {
SelectionOffset previousOffset;
previousOffset = GetPreviousOffset(myLayoutEdit->layout,
oldOffset);
GXSetLayoutParts(myLayoutEdit->layout,
previousOffset,
oldOffset,
0, nil, nil,
0, nil, nil,
0, nil, nil);
GXDrawShape(myLayoutEdit->highlight); /* erase old */
NewSelectionAndHighlight(myLayoutEdit,
previousOffset, previousOffset);
GXDrawShape(myLayoutEdit->highlight); /* draw new */
}If the current selection is a range selection, you need to delete the characters in the selection. You can use this code:
SelectionOffset rangeStart, rangeEnd;
rangeStart = myLayoutEdit->selectionRanges.ranges[0].minOffset;
rangeEnd = myLayoutEdit->selectionRanges.ranges[0].maxOffset;
GXSetLayoutParts(myLayoutEdit->layout,
rangeStart,
rangeEnd,
0, nil, nil,
0, nil, nil,
0, nil, nil);
GXDrawShape(myLayoutEdit->highlight); /* erase old */NewSelectionAndHighlight(myLayoutEdit,
rangeStart, rangeEnd);
GXDrawShape(myLayoutEdit->highlight); /* draw new */- Handle character keys
If the user pressed a character key, you need to insert the character into the text of the layout.
First, however, you need to delete the characters in the current selection if the selection was a range selection, as you did in Step 3:
SelectionOffset rangeStart, rangeEnd;
rangeStart = myLayoutEdit->selectionRanges.ranges[0].minOffset;
rangeEnd = myLayoutEdit->selectionRanges.ranges[0].maxOffset;
GXSetLayoutParts(myLayoutEdit->layout,
rangeStart,
rangeEnd,
0, nil, nil,
0, nil, nil,
0, nil, nil);Now you can use the
GXSetLayoutParts
function to insert the character into the layout:
SelectionOffset currentOffset;
char charToInsert[1];
currOffset = myLayoutEdit->selectionRanges.ranges[0].minOffset;
charToInsert[1] = key;
GXSetLayoutParts(myLayoutEdit->layout,
currentOffset,
currentOffset,
1, 1, &charToInsert,
0, nil, nil,
0, nil, nil);Finally, you can erase the old highlight shape and draw the new one:
GXDrawShape(myLayoutEdit->highlight); /* erase old */NewSelectionAndHighlight(myLayoutEdit,
currentOffset + 1, currentOffset + 1);
Related Recipes
The previous recipe, "Hit-Testing Layout Shapes," shows how you can allow the user to select text in a layout shape by pressing and dragging the mouse.The recipes in Chapter 4, "Using the QuickDraw GX Environment," show you how to initialize QuickDraw GX. You should read the recipes in that chapter before using any recipes in this chapter.
The recipes in Chapter 5, "Using Macintosh Windows," show you how to use QuickDraw GX with Macintosh windows. You need to be familiar with the information in that chapter before you can display layout shapes in a Macintosh window.